﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using Domain.Model.Commons.Atrributes;
using Common.Logging;

namespace Domain.Repository.Impl
{
    public class DataReaderBuilder<T> where T : class
    {
        protected static ILog logger;

        protected IDictionary<string, KeyValuePair<PropertyInfo, object>> mappingInfo;

        public DataReaderBuilder()
        {
            logger = LogManager.GetLogger(GetType());
        }

        public T Builde(IDataReader reader)
        {
            if (reader.FieldCount == 1)
            {
                return reader.GetValue(0) as T;
            }

            GenerateMapping();
            T instance = Activator.CreateInstance<T>();
            for (int i = 0; i < reader.FieldCount; i++)
            {
                string columnName = reader.GetName(i);
                if (mappingInfo.ContainsKey(columnName.ToUpper()))
                {
                    var columnValue = reader.GetValue(i);

                    //logger.Info("Get value : " + columnValue);
                    if (DBNull.Value.Equals(columnValue))
                    {
                        continue;
                    }

                    KeyValuePair<PropertyInfo, object> mapItem = mappingInfo[columnName.ToUpper()];
                    SetValue(mapItem, instance, columnValue);
                }
            }

            return instance;
        }

        protected void SetValue(KeyValuePair<PropertyInfo, object> mapItem, T target, object value)
        {
            logger.Debug("=====>>Target : " + target + "\tValue : " + value);
            if (null == target || null == value)
            {
                return;
            }

            ManyToOne manyToOne = mapItem.Value as ManyToOne;
            object instance = null;
            if (null != manyToOne)
            {
                Type member = mapItem.Key.PropertyType;
                PropertyInfo propertyMember = member.GetProperty("Id");
                if (null != propertyMember)
                {
                    var memberValue = Convert.ChangeType(value, propertyMember.PropertyType);
                    instance = Activator.CreateInstance(member);
                    propertyMember.SetValue(instance, memberValue, null);
                }
            }
            else if (mapItem.Key.PropertyType.IsEnum)
            {
                instance = Enum.Parse(mapItem.Key.PropertyType, value.ToString());
            }
            else
            {
                instance = Convert.ChangeType(value, mapItem.Key.PropertyType);
            }
            mapItem.Key.SetValue(target, instance, null);
        }

        protected void GenerateMapping()
        {
            if (null == mappingInfo || mappingInfo.Count == 0)
            {
                mappingInfo = new Dictionary<string, KeyValuePair<PropertyInfo, object>>();
            }
            else
            {
                return;
            }

            Type targetType = typeof(T);
            foreach (PropertyInfo entry in targetType.GetProperties())
            {
                var attrItems = entry.GetCustomAttributes(false);
                string columnName = null;
                if (null != attrItems && attrItems.Length > 0)
                {
                    foreach (object item in attrItems)
                    {
                        KeyValuePair<PropertyInfo, object> pair = new KeyValuePair<PropertyInfo, object>(entry, item);
                        columnName = MappingColumn(pair);
                        logger.Debug("=====>>Find model column : " + columnName + "\tProperty : " + entry.DeclaringType + "." + entry.Name);
                        if (!String.IsNullOrWhiteSpace(columnName) && !mappingInfo.ContainsKey(columnName.ToUpper()))
                        {
                            mappingInfo.Add(columnName.ToUpper(), pair);
                            break;
                        }
                    }
                }
                if (String.IsNullOrWhiteSpace(columnName))
                {
                    mappingInfo.Add(entry.Name.ToUpper(), new KeyValuePair<PropertyInfo, object>(entry, null));
                }
            }
        }

        protected string MappingColumn(KeyValuePair<PropertyInfo, object> source)
        {
            var primaryKey = source.Value as PrimaryKey;
            if (null != primaryKey)
            {
                return primaryKey.ColumnName;
            }
            var property = source.Value as Property;
            if (null != property)
            {
                return property.ColumnName;
            }
            var version = source.Value as Domain.Model.Commons.Atrributes.Version;
            if (null != version)
            {
                return version.ColumnName;
            }
            var manyToOne = source.Value as ManyToOne;
            if (null != manyToOne)
            {
                return manyToOne.ColumnName;
            }
            return null;
        }
    }
}
